////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_INTERNAL_ARM_NEON_ELEMENTS_H__
#define __WN_MATH_INTERNAL_ARM_NEON_ELEMENTS_H__

#ifndef __WN_MATH_ELEMENTS_H__
    #error "Internal/ARM/NEON/WNElements.h should never be included directly."
#elif !defined _WN_ARM
    #error "Internal/ARM/NEON/WNElements.h has been included on a non ARM platform. Please rectify this."
#elif !defined __WN_MATH_ENABLE_EXTENSIONS
    #error "Internal/ARM/NEON/WNElements.h has been included when extensions are disabled. Please rectify this."
#elif !defined __WN_MATH_NEON_ALLOWED
    #error "Internal/ARM/NEON/WNElements.h has been included when NEON is not allowed. Please rectify this."
#endif

#include <arm_neon.h>

namespace WNMath {
    namespace __WNInternal {
        #ifdef __WN_MATH_NEON_AVAILABLE
            template <WN_UINT32 Dimension>
            struct WN_ALIGN(16) __WNElements<WN_FLOAT32, Dimension, typename WNCore::WNEnableWhen<(Dimension == 2)>::Value> {
                WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                union {
                    WN_FLOAT32 mValues[Dimension];
                    float32x2_t mDValues[(Dimension + 1) >> 1];
                };
            };

            template <WN_UINT32 Dimension>
            struct WN_ALIGN(16) __WNElements<WN_FLOAT32, Dimension, typename WNCore::WNEnableWhen<(Dimension > 2)>::Value> {
                WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                union {
                    WN_FLOAT32 mValues[Dimension];
                    float32x4_t mQValue[(Dimension + 3) >> 2];
                    float32x2_t mDValue[(Dimension + 1) >> 1];
                };
            };
        #else
            template <WN_UINT32 Dimension>
            struct WN_ALIGN(16) __WNElements<WN_FLOAT32, Dimension, typename WNCore::WNEnableWhen<(Dimension > 1)>::Value> {
                WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                WN_FLOAT32 mValues[Dimension];
            };
        #endif
    }
}

#endif // __WN_MATH_INTERNAL_ARM_NEON_ELEMENTS_H__